package com.yzq.manager;


import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.yzq.enums.ErrorCode;
import com.yzq.exception.BusinessException;
import com.yzq.utils.AliyunOSSUtils;
import com.yzq.utils.ThrowUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

/**
 * 文件图片上传管理类，提供本地图片文件和图片 URL 上传的功能，
 * 并在上传前对文件或 URL 进行格式、大小、类型等校验。
 */
@Slf4j
@Component
public class FilePictureUpload {

    @Resource
    private AliyunOSSUtils aliyunOSSUtils;

    // 允许的图片格式
    private static final List<String> ALLOWED_FORMATS = Arrays.asList("jpg", "jpeg", "png", "gif");
    // 最大文件大小，单位：字节
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024;

    /**
     * 上传本地图片文件到阿里云 OSS。
     * 在上传前会对文件进行校验，包括文件是否为空、文件大小是否超过限制、文件格式是否支持。
     *
     * @param file 待上传的图片文件，使用 Spring 的 MultipartFile 类型
     * @param category 图片类别，null 表示公共图库，spaceId 表示私有图库
     * @return 图片上传到阿里云 OSS 后的访问地址
     * @throws Exception 当文件校验不通过或上传过程中出现异常时抛出
     */
    public String uploadLocalFile(MultipartFile file, Long category) throws Exception {
        // 调用本地文件校验方法，确保文件符合上传要求
        validateLocalFile(file);
        // 读取文件内容为字节数组，用于后续上传
        byte[] content = file.getBytes();
        // 获取文件的原始文件名，用于生成新的文件名
        String originalFilename = file.getOriginalFilename();
        Long spaceId = category == null ? 0 : category;
        // 调用阿里云 OSS 工具类的上传方法，将文件上传到指定类别图库
        if (originalFilename != null) {
            return aliyunOSSUtils.upload(content, originalFilename, spaceId);
        } else {
            return null;
        }
    }

    /**
     * 上传图片 URL 对应的图片到阿里云 OSS。
     * 在上传前会对 URL 进行校验，包括 URL 格式、协议、文件是否存在、文件大小和文件格式。
     *
     * @param url 图片的 URL 地址
     * @param category 图片类别，0 表示公共图库，spaceId 表示私有图库
     * @return 图片上传到阿里云 OSS 后的访问地址
     * @throws Exception 当 URL 校验不通过或上传过程中出现异常时抛出
     */
    public String uploadUrl(String url, Long category) throws Exception {
        // 调用 URL 校验方法，确保 URL 符合上传要求
        validateUrl(url);
        // 从 URL 下载文件内容为字节数组，用于后续上传
        byte[] content = downloadFileFromUrl(url);
        // 从 URL 中提取文件名，用于生成新的文件名
        String originalFilename = extractFileNameFromUrl(url);
        Long spaceId = category == null ? 0 : category;
        // 调用阿里云 OSS 工具类的上传方法，将文件上传到指定类别图库
        return aliyunOSSUtils.upload(content, originalFilename, spaceId);
    }

    /**
     * 校验本地文件是否符合上传要求。
     * 校验内容包括文件是否为空、文件大小是否超过限制、文件格式是否支持。
     *
     * @param file 待校验的本地图片文件
     * @throws Exception 当文件校验不通过时抛出，包含具体的错误信息
     */
    private void validateLocalFile(MultipartFile file) throws Exception {
        // 检查文件是否为空
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "文件不能为空");
        }
        // 获取文件大小
        long fileSize = file.getSize();
        // 检查文件大小是否超过最大限制
        if (fileSize > MAX_FILE_SIZE) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "文件大小超过限制");
        }
        // 获取文件扩展名
        String fileExtension = getFileExtension(file.getOriginalFilename());
        // 检查文件格式是否为允许的格式
        if (!ALLOWED_FORMATS.contains(fileExtension.toLowerCase())) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的文件格式");
        }
    }

    /**
     * 校验图片 URL 是否符合上传要求。
     * 校验内容包括 URL 格式、协议、文件是否存在、文件大小和文件格式。
     *
     * @param fileUrl 待校验的图片 URL 地址
     * @throws Exception 当 URL 校验不通过时抛出，包含具体的错误信息
     */
    private void validateUrl(String fileUrl) throws Exception {
        ThrowUtils.throwIf(fileUrl == null, ErrorCode.BAD_REQUEST, "图片 URL 不能为空");
        URL url;
        try {
           url = new URL(fileUrl);
        } catch (MalformedURLException e) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "图片 URL 格式错误");
        }
        // 校验图片 URL 协议
        ThrowUtils.throwIf(!(fileUrl.startsWith("http://") || fileUrl.startsWith("https://")), ErrorCode.BAD_REQUEST, "图片 URL 协议错误");

        try (HttpResponse httpResponse = HttpUtil.createRequest(Method.HEAD, fileUrl).execute()) {
            if (httpResponse.getStatus() != HttpStatus.HTTP_OK) {
                throw new BusinessException(ErrorCode.BAD_REQUEST, "图片 URL 错误");
            }
            String header = httpResponse.header("Content-Type");
            if (StrUtil.isNotBlank(header)) {
                final List<String> ALLOW_CONTENT_TYPE = Arrays.asList("image/jpeg", "image/png", "image/gif", "image/webp", "image/jpg");
                ThrowUtils.throwIf(!ALLOW_CONTENT_TYPE.contains(header.toLowerCase()), ErrorCode.BAD_REQUEST, "图片 URL 错误");
            }

            //校验图片大小
            String header1 = httpResponse.header("Content-Length");
            try {
                if (StrUtil.isNotBlank(header1)) {
                    ThrowUtils.throwIf(Long.parseLong(header1) > MAX_FILE_SIZE, ErrorCode.BAD_REQUEST, "图片大小超过 10MB");
                }
            } catch (NumberFormatException e) {
                throw new BusinessException(ErrorCode.BAD_REQUEST, "图片大小格式错误");
            }
        }
    }



    /**
     * 从指定 URL 下载文件内容。
     *
     * @param url 图片的 URL 地址
     * @return 文件内容的字节数组
     * @throws IOException 当下载过程中出现网络或 I/O 错误时抛出
     */
    private byte[] downloadFileFromUrl(String url) throws IOException {
        URL fileUrl = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();
        connection.setRequestMethod("GET");
        connection.setConnectTimeout(5000); // 设置连接超时时间为 5 秒
        connection.setReadTimeout(5000);    // 设置读取超时时间为 5 秒

        int responseCode = connection.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "无法下载文件，响应码: " + responseCode);
        }

        try (InputStream inputStream = connection.getInputStream();
             ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
            int nRead;
            byte[] data = new byte[1024];
            while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
            buffer.flush();
            return buffer.toByteArray();
        }
    }


    /**
     * 从 URL 中提取文件名。
     * 处理两种情况: 1、带文件后缀 2、不带文件后缀
     * @param url 图片的 URL 地址
     * @return 从 URL 中提取的文件名
     */
    private String extractFileNameFromUrl(String url) {
        // 从 URL 中截取最后一个斜杠后的部分作为文件名
        String path = url.substring(url.lastIndexOf("/") + 1);
        // 检查文件名中是否包含扩展名
        if (path.contains(".")) {
            String extension = path.substring(path.lastIndexOf(".") + 1);
            if (ALLOWED_FORMATS.contains(extension.toLowerCase())) {
                return path;
            }
        }
        // 如果没有有效的扩展名，尝试从 HTTP 响应头中获取文件类型
        try {
            URL fileUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();
            connection.setRequestMethod("HEAD");
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                String contentType = connection.getContentType();
                String extension = getExtensionFromContentType(contentType);
                if (extension != null) {
                    return path + "." + extension;
                }
            }
        } catch (IOException e) {
            // 忽略异常，返回原始路径
            log.error("获取文件扩展名失败：{}", e.getMessage());
            return path;
        }
        return path;
    }

    /**
     * 根据 Content-Type 获取文件扩展名
     *
     * @param contentType HTTP 响应头中的 Content-Type
     * @return 文件扩展名，如果不支持则返回 null
     */
    private String getExtensionFromContentType(String contentType) {
        if (contentType != null) {
            if (contentType.contains("image/jpeg")) {
                return "jpg";
            } else if (contentType.contains("image/png")) {
                return "png";
            } else if (contentType.contains("image/gif")) {
                return "gif";
            } else if (contentType.contains("image/webp")) {
                return "webp";
            } else if (contentType.contains("image/jpg")) {
                return "jpg";
            }
        }
        return null;
    }

    /**
     * 获取文件的扩展名。
     *
     * @param fileName 文件名
     * @return 文件的扩展名，如果文件名中不包含扩展名则返回空字符串
     */
    private String getFileExtension(String fileName) {
        if (fileName == null) {
            return "";
        }
        // 查找文件名中最后一个点的位置
        int lastIndex = fileName.lastIndexOf(".");
        // 如果找到点，则返回点后面的部分作为扩展名，否则返回空字符串
        return lastIndex == -1 ? "" : fileName.substring(lastIndex + 1);
    }
}
